package com.kakarote.ids.provider.common;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.kakarote.common.exception.BusinessException;
import com.kakarote.common.redis.Redis;
import com.kakarote.common.result.Result;
import com.kakarote.common.result.SystemCodeEnum;
import com.kakarote.common.servlet.ApplicationContextHolder;
import com.kakarote.ids.provider.entity.VO.TokenVO;
import feign.FeignException;
import feign.Logger;
import feign.Request;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.Response;
import feign.RetryableException;
import feign.Retryer;
import feign.codec.Decoder;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.HttpMessageConverterCustomizer;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

public class FeignConfig {
  @Bean
  public Decoder feignDecoder(ObjectProvider<HttpMessageConverterCustomizer> customizers, ObjectFactory<HttpMessageConverters> messageConverters) {
    return (Decoder)new ResponseEntityDecoder(new FeignDecode(new SpringDecoder(messageConverters, customizers)));
  }
  
  @Bean
  Retryer getRetryBean() {
    return new ReLoginStrategy();
  }
  
  @Bean
  public Logger.Level loggerLevel() {
    return Logger.Level.FULL;
  }
  
  @Bean
  public RequestInterceptor interceptor() {
    return requestTemplate -> {
        Redis redis = (Redis)ApplicationContextHolder.getBean(Redis.class);
        ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
          HttpServletRequest request = attributes.getRequest();
          String token = request.getHeader("AUTH-TOKEN");
          if (StrUtil.isNotEmpty(token) && redis.exists(token + "_token")) {
            TokenVO data = (TokenVO)redis.get(token + "_token");
            requestTemplate.header("Authorization", new String[] { "Bearer " + data.getAccessToken() });
          } 
        } else {
          OAuthProperties oAuthProperties = (OAuthProperties)ApplicationContextHolder.getBean(OAuthProperties.class);
        } 
      };
  }
  
  public static class FeignDecode implements Decoder {
    private final SpringDecoder decoder;
    
    FeignDecode(SpringDecoder decoder) {
      this.decoder = decoder;
    }
    
    public Object decode(Response response, Type type) throws FeignException, IOException {
      Object data = this.decoder.decode(response, type);
      if (data instanceof Result) {
        if (((Result)data).getCode().equals(Integer.valueOf(SystemCodeEnum.SYSTEM_NOT_LOGIN.getCode())))
          throw new RetryableException(SystemCodeEnum.SYSTEM_NOT_LOGIN.getCode(), SystemCodeEnum.SYSTEM_NOT_LOGIN.getMsg(), Request.HttpMethod.POST, new Date(), response.request()); 
        if (!((Result)data).hasSuccess())
          throw new BusinessException(((Result)data).getCode(), ((Result)data).getMsg()); 
      } 
      return data;
    }
  }
  
  public static class ReLoginStrategy implements Retryer {
    private boolean initToken = true;
    
    public void continueOrPropagate(RetryableException e) {
      if (SystemCodeEnum.SYSTEM_NOT_LOGIN.getCode() == e.status() && this.initToken) {
        Redis redis = (Redis)ApplicationContextHolder.getBean(Redis.class);
        Collection<String> collection = (Collection<String>)e.request().headers().get("AUTH-TOKEN");
        if (collection == null || collection.isEmpty())
          throw new BusinessException(SystemCodeEnum.SYSTEM_NOT_LOGIN); 
        String token = (String)collection.iterator().next() + "_token";
        if (!redis.exists(token))
          throw new BusinessException(SystemCodeEnum.SYSTEM_NOT_LOGIN); 
        TokenVO tokenVO = (TokenVO)redis.get(token);
        OAuthProperties properties = (OAuthProperties)ApplicationContextHolder.getBean(OAuthProperties.class);
        HttpRequest request = HttpUtil.createPost("https://id.72crm.com/open/oauth2/token");
        JSONObject object = new JSONObject();
        object.put("clientId", properties.getClientId());
        object.put("clientSecret", properties.getClientSecret());
        object.put("redirectUri", properties.getRedirectUri());
        object.put("scope", "user");
        object.put("grantType", "refresh_token");
        object.put("refreshToken", tokenVO.getRefreshToken());
        try {
          request.body(object.toJSONString());
          HttpResponse execute = request.execute();
          JSONObject body = JSON.parseObject(execute.body());
          Result<TokenVO> result = Result.of(body.getInteger("code").intValue(), body.getString("msg"));
          if (result.hasSuccess()) {
            TokenVO data = (TokenVO)body.getObject("data", TokenVO.class);
            redis.setex(token, Integer.valueOf(redis.ttl(token).intValue()), data);
          } 
        } catch (Exception exception) {}
        this.initToken = false;
      } 
    }
    
    public Retryer clone() {
      return new ReLoginStrategy();
    }
  }
}
